/*
 * $Id: $
 */
package org.rcgwt.client.ui.grid.renderer;

import org.rcgwt.client.theme.CssStyle;
import org.rcgwt.client.ui.grid.internal.ShowModelElementDelta;
import org.rcgwt.client.ui.grid.internal.model.ICellModel;
import org.rcgwt.client.ui.grid.internal.model.IElementModel;
import org.rcgwt.client.ui.grid.internal.model.IElementModelDelta;
import org.rcgwt.client.ui.grid.internal.model.IGroupModel;
import org.rcgwt.client.ui.grid.internal.model.IHeaderModel;
import org.rcgwt.client.ui.grid.internal.model.IRowModel;
import org.rcgwt.client.ui.grid.internal.model.ITitleCellModel;
import org.rcgwt.client.ui.grid.internal.model.ITitleRowModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModelElements;
import org.rcgwt.client.ui.grid.theme.IGridTheme;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.impl.FocusImpl;

/**
 * 
 * @author Olivier Oeuillot (latest modification by $Author: $)
 * @version $Revision: $ $Date: $
 */
abstract class AbstractRenderer<T extends IElementModel> {

	private static final FocusImpl impl = FocusImpl.getFocusImplForPanel();

	public void processDelta(IViewRenderContext viewRenderContext,
			Element element, IElementModelDelta<T> modelElementDelta) {

		T modelElement = modelElementDelta.getModelElement();

		if (modelElementDelta.hasStateMaskChanged()) {

			int masks = modelElementDelta.getMask();
			int bits = modelElementDelta.getBits();

			updateStateMask(viewRenderContext, element, modelElement);
		}

		if (modelElementDelta.hasContentChanged()) {
			updateContent(viewRenderContext, element, modelElement);
		}

		processDelta(viewRenderContext, modelElementDelta);

		if (modelElementDelta instanceof ShowModelElementDelta<?>) {
			IElementModel showElementModel = ((ShowModelElementDelta<?>) modelElementDelta)
					.getShowElementModel();
			if (showElementModel != null) {
				showElementModel(viewRenderContext, element, modelElement,
						showElementModel);
			}
		}
	}

	public boolean showElementModel(IViewRenderContext viewRenderContext,
			Element element, T modelElement, IElementModel showModelElement) {

		return false;
	}

	public void update(IViewRenderContext viewRenderContext, Element element,
			T modelElement, int renderedIndex) {
		updateContent(viewRenderContext, element, modelElement);

		updateStateMask(viewRenderContext, element, modelElement);
	}

	protected abstract void updateContent(IViewRenderContext viewRenderContext,
			Element element, T modelElement);

	protected void updateStateMask(IViewRenderContext viewRenderContext,
			Element element, T modelElement) {

		CssStyle cssStyle = getCssStyle(viewRenderContext, modelElement);
		if (cssStyle != null) {
			element.setClassName(cssStyle.getClassName());
		}

	}

	protected CssStyle getCssStyle(IViewRenderContext viewRenderContext,
			T modelElement) {
		return null;
	}

	@SuppressWarnings( { "unchecked", "incomplete-switch" })
	protected static void processDelta(IViewRenderContext viewRenderContext,
			IElementModelDelta<?> modelElementDelta) {
		IGridRenderContext gridRenderContext = viewRenderContext
				.getGridRenderContext();
		IViewModel viewModel = viewRenderContext.getViewModel();
		IViewModelElements viewModelElements = viewModel.getViewModelElements();

		IGridTheme theme = viewRenderContext.getGridRenderContext().getTheme();

		IElementModelDelta<? extends IElementModel> deltas[] = modelElementDelta
				.listChildrenDeltas();

		for (IElementModelDelta<? extends IElementModel> delta : deltas) {
			if (delta == null) {
				continue;
			}

			IElementModel child = delta.getModelElement();

			switch (child.getType()) {
			case CELL:
				ICellModel cell = (ICellModel) child;

				Element cellElement = viewModelElements.getCellElement(
						gridRenderContext, cell);
				if (cellElement == null) {
					continue;
				}

				IRenderer<ICellModel> cellRenderer = theme
						.getCellRenderer(cell);

				cellRenderer.processDelta(viewRenderContext, cellElement,
						(IElementModelDelta<ICellModel>) delta);
				break;

			case ROW:
				IRowModel row = (IRowModel) child;

				Element rowElement = viewModelElements.getRowElement(row);
				if (rowElement == null) {
					continue;
				}

				IRowRenderer rowRenderer = theme.getRowRenderer(row);

				rowRenderer.processDelta(viewRenderContext, rowElement,
						(IElementModelDelta<IRowModel>) delta);
				break;

			case GROUP:
				IGroupModel groupModel = (IGroupModel) child;

				Element groupElement = viewModelElements
						.getGroupElement(groupModel);
				if (groupElement == null) {
					continue;
				}

				IGroupRenderer groupRenderer = theme
						.getGroupRenderer(groupModel);

				groupRenderer.processDelta(viewRenderContext, groupElement,
						(IElementModelDelta<IGroupModel>) delta);
				break;

			case TITLE_ROW:
				ITitleRowModel titleRow = (ITitleRowModel) child;

				Element titleRowElement = viewModelElements
						.getTitleRowElement(titleRow);
				if (titleRowElement == null) {
					continue;
				}

				ITitleRowRenderer titleRowRenderer = theme
						.getTitleRowRenderer(titleRow);

				titleRowRenderer.processDelta(viewRenderContext,
						titleRowElement,
						(IElementModelDelta<ITitleRowModel>) delta);
				break;

			case TITLE_CELL:
				ITitleCellModel titleCell = (ITitleCellModel) child;

				Element titleCellElement = viewModelElements
						.getTitleCellElement(titleCell);
				if (titleCellElement == null) {
					continue;
				}

				ITitleCellRenderer titleCellRenderer = theme
						.getTitleCellRenderer(titleCell);

				titleCellRenderer.processDelta(viewRenderContext,
						titleCellElement,
						(IElementModelDelta<ITitleCellModel>) delta);
				break;

			case HEADER:
				IHeaderModel header = (IHeaderModel) child;

				Element headerElement = viewModelElements
						.getHeaderElement(header);
				if (headerElement == null) {
					continue;
				}

				IHeaderRenderer headerRenderer = theme
						.getHeaderRenderer(header);

				headerRenderer.processDelta(viewRenderContext, headerElement,
						(IElementModelDelta<IHeaderModel>) delta);
				break;

			case COLUMN:
				// RIEN ....
				break;

			case VIEW:
			case GRID:
				// RIEN
				break;
			}
		}
	}

	protected final void processDelta(IGridRenderContext gridRenderContext,
			IElementModelDelta<?> modelElementDelta) {

		IGridTheme theme = gridRenderContext.getTheme();

		IElementModelDelta<? extends IElementModel> deltas[] = modelElementDelta
				.listChildrenDeltas();

		for (IElementModelDelta<? extends IElementModel> delta : deltas) {
			if (delta == null) {
				continue;
			}

			IElementModel child = delta.getModelElement();

			switch (child.getType()) {

			case VIEW:
				IViewModel viewModel = (IViewModel) child;

				Element viewElement = viewModel.getViewModelElements()
						.getViewElement();
				if (viewElement == null) {
					continue;
				}

				IViewRenderer viewRenderer = theme.getViewRenderer(viewModel);

				IViewRenderContext viewRendererContext = gridRenderContext
						.getViewRenderContext(viewModel);

				viewRenderer.processDelta(viewRendererContext, viewElement,
						(IElementModelDelta<IViewModel>) delta);
				break;
			}
		}
	}

	protected final com.google.gwt.user.client.Element toElement(Element element) {
		return (com.google.gwt.user.client.Element) element;
	}

	public int[] getSizes(IViewRenderContext viewRenderContext,
			Element element, T modelElement, int sizes[]) {
		if (sizes == null || sizes.length < 2) {
			sizes = new int[2];
		}

		sizes[0] = element.getOffsetWidth();
		sizes[1] = element.getOffsetHeight();

		return sizes;
	}

	protected final Element createFocusableElement(Document document) {
		return impl.createFocusable();
	}

	protected final void focusElement(Element element) {
		impl.focus((com.google.gwt.user.client.Element) element);
	}

}
